package com.wiringweb.resolution;

import com.gooddies.utils.IterableEnumeration;
import com.wiringweb.exceptions.WiringWebException;
import com.wiringweb.exceptions.WiringWebNotFound;
import com.wiringweb.servlet.WiringRequestProcessor;
import java.io.IOException;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletConfig;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.app.event.EventCartridge;
import org.apache.velocity.app.event.IncludeEventHandler;
import org.apache.velocity.app.event.ReferenceInsertionEventHandler;
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;

/**
 * @author Dmitry
 */
public class VelocityResolution implements Resolution {

    private static String basePath = "";
    private final String path;
    static boolean isInit = false;
    private static final Map<String, Object> emptyMap = new HashMap<String, Object>() {
        @Override
        public Object put(String key, Object value) {
            return value;
        }
    };

    public static class MyIncludeHandler implements IncludeEventHandler {

        @Override
        public String includeEvent(String includeResourcePath, String currentResourcePath, String directiveName) {
            return basePath + includeResourcePath;
        }
    };

    public synchronized static void init() {
        if (!isInit) {
            Velocity.setProperty("resource.loader", "class");
            Velocity.setProperty("eventhandler.include.class", MyIncludeHandler.class.getName());
            Velocity.setProperty("class.resource.loader.class",
                    ClasspathResourceLoader.class.getName());
            Velocity.init();
            isInit = true;
        }
    }

    public VelocityResolution(String path) {
        this.path = path;
    }

    public static void setBasePath(String basePath) {
        VelocityResolution.basePath = basePath;
    }

    @Override
    public void process(HttpServletRequest request, HttpServletResponse response, WiringRequestProcessor requestProcessor, ServletConfig servletConfig) throws IOException {
        Template template = null;
        final String velocityTemplatePath = basePath + path;
        try {
            template = Velocity.getTemplate(velocityTemplatePath, "UTF-8");
        } catch (Exception ex) {
            throw new WiringWebNotFound("Template " + velocityTemplatePath + " is not found");
        }

        VelocityContext context = new VelocityContext();
        context.attachEventCartridge(new EventCartridge());
        context.getEventCartridge().addReferenceInsertionEventHandler(new ReferenceInsertionEventHandler() {
            @Override
            public Object referenceInsert(String reference, Object value) {
                if (value == null) {
                    if (reference.startsWith("$_")) {
                        return "";
                    }
                    throw new WiringWebException("Velocity template " + velocityTemplatePath + " variable " + reference + " is not set.");
                }
                return value;
            }
        });
        HttpSession session = request.getSession(false);
        if (!(session == null || session.getAttribute("data") == null)) {
            context.put("session", session.getAttribute("data"));
        }
        for (String key : IterableEnumeration.make(request.getAttributeNames())) {
            if (key.equals("session")) {
                continue;
            }

            Object value = request.getAttribute(key);
            context.put(key, value);
        }
        response.setStatus(200);
        response.setContentType("text/html;charset=UTF-8");
        Writer writer = response.getWriter();
        template.merge(context, writer);
    }
}
